// Copyright 2009 Jan Hosang <jan.hosang@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package invocation

import (
    "container/list"
    "fmt"
    "strings"
    
    "gb/tools"
)

type Invocation struct {
    Label string
    Targets, Sources *list.List
}

var atomicInvocation = map[string] bool{
    "Depends": true,
    "Exists": true,
    "Shell": true,
    "SubDir": true,
}


func New(label string, targets, sources *list.List) *Invocation {
    return &Invocation{label, targets, sources}
}

func (inv *Invocation) String() string {
    return fmt.Sprintf("%s %s : %s", inv.Label, tools.ListToString(inv.Targets), tools.ListToString(inv.Sources))
}

// Checks if an invocation needs anymore matching with recipes.
func (inv *Invocation) IsAtomic() bool {
    _, ok := atomicInvocation[inv.Label]
    return ok
}

func (inv *Invocation) ReplaceVariables(perc bool, percRepl string, vars map[string] *list.List) *Invocation {
    return New(inv.Label,
            replaceStringListVariables(inv.Targets, perc, percRepl, vars),
            replaceStringListVariables(inv.Sources, perc, percRepl, vars),
        )
}

// Iterate through a list of strings (targets or sources) and replace
// percentage signs with percRepl (if perc == true) and known variables
// by their value.
func replaceStringListVariables(l *list.List, perc bool, percRepl string, vars map[string] *list.List) *list.List {
    if l == nil {
        return nil
    }
    
    r := list.New()
    for el := range l.Iter() {
        s := el.(string)
        
        // deal with percentages
        if perc {
            s = ReplacePerc(s, percRepl)
        }

        // deal with variables
        substrs := strings.Split(s, "$", 0)
        if len(substrs) == 2 && len(substrs[0]) == 0 && strings.HasSuffix(substrs[1], ")") {
            // the complete string s is only one variable
            // we expand the list behind the variable into the list l
            key := substrs[1]
            key = key[1:len(key)-1]
            if valList, ok := vars[key]; ok {
                for el := range valList.Iter() {
                    r.PushBack(el)
                }
                continue
            }
        } else {
            // we have more than one plain variable
            // in this case we allow each variable only to contain on value each
            for i := 1; i < len(substrs); i++ {
                // the parser ensures a variable has the form "$(STRING)"
                substr := substrs[i]
                end := strings.Index(substr, ")")
                key := substr[1:end]
                val, ok := vars[key]

                var repl string
                if !ok {
                    //fmt.Printf("Warning: Encountered unbound variable $(%s). Using empty value.\n", key)
                    repl = "$("+key+")"
                } else if val.Len() > 2 {
                    fmt.Printf("Warning: Multi-valued variable $(%s) used in single value context. Using values as space joined string.\n", key)
                    mvalue := make([]string, val.Len())
                    i := 0
                    for el := range val.Iter() {
                        mvalue[i] = el.(string)
                        i++
                    }
                    repl = strings.Join(mvalue, " ")
                } else {
                    repl = val.Front().Value.(string)
                }
                substrs[i] = repl+substr[end+1:]
            }
            s = strings.Join(substrs, "")
        }
        
        r.PushBack(s)
    }
    return r
}

func ReplacePerc(str, repl string) string {
    split := strings.Split(str, "%", 2)
    
    if len(split) == 1 {
        // no % found in str
        return str
    }
    
    return split[0]+repl+split[1]
}
